<!--
 * @Description: ------------ fileDescription -----------
 * @Author: snows_l snows_l@163.com
 * @Date: 2024-08-05 12:46:00
 * @LastEditors: snows_l snows_l@163.com
 * @LastEditTime: 2025-11-05 16:53:23
 * @FilePath: \BLOG\src\views\home\index.vue
-->
<template>
  <div class="home-wrap" :class="{ 'm-home-wrap': isMobi }">
    <!-- 首屏 -->
    <FirstScreen />
    <div class="other-content-wrap">
      <Notice class="notice" v-if="isMobi" :class="{ 'm-notice': isMobi }"></Notice>
      <div class="flex-wrap">
        <div class="content-center">
          <Notice class="notice" v-if="!isMobi" :class="{ 'm-notice': isMobi }"></Notice>
          <div class="other-content" :class="{ 'm-other-content': isMobi }">
            <div class="other-content-item">
              <div class="article-title-wrap">
                <img class="article-icon" src="@/assets/images/common/article.png" alt="" />
                <h2 class="content-title">ARTICLE</h2>
              </div>
              <!-- 文章列表 -->
              <ArticleList
                type="home"
                :articleList="articleList"
                :loading="state.loading"
                :isMobi="isMobi"
                :articleTypeList="articleTypeList"
                :colorMap="colorMap"
                :avatar="state.avatar"
                :page="state.page"
                @article-click="handleArticle"
                @view-click="handleView"
                @type-click="handleType"
                @category-click="handleCategory" />
            </div>
          </div>

          <LoadingMore v-if="isLoadMoreMode" :loading="state.loading" :hasMore="state.isMore" :length="articleList.length" @loadMore="handleLoadMore"></LoadingMore>
          <CustomPage v-else :page="state.page" @change="handlePageChange" :loading="state.loading" :offsetHeight="isMobi ? 20 : 100" scorllTop="content-center"></CustomPage>
        </div>
        <div class="slider-wrap" v-if="!state.isMobile || isMobi">
          <div class="position-wrap">
            <!-- 我 -->
            <AuthInfo :page="state.page" :cateLen="articleTypeList.length || 0" class="slider-item-wrap" :class="{ 'intersection-observer': isMobi }" />

            <!-- 访问信息 -->
            <VisitInfo class="slider-item-wrap" :class="{ 'intersection-observer': isMobi }" />

            <!-- 热门文章 -->
            <HotArticle class="slider-item-wrap" :class="{ 'intersection-observer': isMobi }" />

            <!-- (❁´◡`❁) 舔狗日记 -->
            <DogLog class="slider-item-wrap" :class="{ 'intersection-observer': isMobi }" />

            <!-- 我的标签 -->
            <MyLabels class="slider-item-wrap" :class="{ 'intersection-observer': isMobi }" />

            <!-- 访问统计 -->
            <VisitTatal class="slider-item-wrap" :class="{ 'intersection-observer': isMobi }" />
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { getArticleList } from '@/api/article';
import { getDict } from '@/api/common';
import { useAppStore } from '@/store/app';
import { getOnlineImgUrl, getQQAvatar, isMobile } from '@/utils/common';
import { debounce, throttle } from '@/utils/pref.ts';
import { live2dReloadElement } from '@/utils/live2d';
import { getRandomColor } from '@/utils/theme';
import { onMounted, onUnmounted, onUpdated, reactive, computed, ref, shallowRef } from 'vue';
import { useRouter } from 'vue-router';
import useResize from '@/hooks/useResize';
import moment from 'moment';
import Notice from '@/components/Notice/index.vue';
import useIntersectionObserver from '@/hooks/useIntersectionObserver';
import AuthInfo from './components/AuthInfo.vue';
import VisitInfo from './components/VisitInfo.vue';
import HotArticle from './components/HotArticle.vue';
import DogLog from './components/DogLog.vue';
import MyLabels from './components/MyLabels.vue';
import VisitTatal from './components/visitTatal.vue';
import FirstScreen from './components/FirstScreen.vue';
import ArticleList from '@/components/ArticleList/index.vue';

useIntersectionObserver();
const { isMobi } = useResize();
const router = useRouter();
const store = useAppStore();
// 使用shallowRef优化大数组的响应式性能
const articleList = shallowRef<any[]>([]);
const articleTypeList = shallowRef<any[]>([]);

const state = reactive({
  avatar: getQQAvatar(),
  loading: false,
  isMore: true,
  isMobile: isMobile(1260),
  page: {
    page: 1,
    size: 10,
    total: 0
  }
});

// 使用computed优化计算属性
const isLoadMoreMode = computed(() => store.projectConfig.pageType === 'more');

// 预生成颜色映射，避免重复计算
const colorMap = Object.freeze({
  0: getRandomColor(),
  1: getRandomColor(),
  2: getRandomColor(),
  3: getRandomColor(),
  4: getRandomColor(),
  5: getRandomColor(),
  6: getRandomColor(),
  7: getRandomColor(),
  8: getRandomColor(),
  9: getRandomColor()
});

// 使用节流优化路由跳转
const throttledRouterPush = throttle((path: string, query: any) => {
  router.push({ path, query });
}, 300);

// 优化数据处理函数
const processArticleData = (data: any[]) => {
  return data.map(item => ({
    ...item,
    createTime: moment(item.createTime).format('YYYY-MM-DD HH:mm:ss'),
    cover: import.meta.env.VITE_CURRENT_ENV === 'dev' ? import.meta.env.VITE_DEV_BASE_SERVER + item.cover : store.projectConfig.bsServerUrl + item.cover,
    subTitle: item.subTitle?.replace(/&#39;/g, "'") || '',
    labels: item.labels ? item.labels.split(',') : []
  }));
};

// 获取文章列表
const getArticleListFn = (scorllTopCallback?: Function) => {
  if (state.loading) return; // 防止重复请求

  state.loading = true;
  getArticleList(state.page)
    .then(res => {
      if (res.code === 200) {
        const processedData = processArticleData(res.data);
        if (isLoadMoreMode.value) {
          articleList.value = [...articleList.value, ...processedData];
        } else {
          articleList.value = processedData;
        }
        state.page.total = res.total;
        state.isMore = res.data.length > 0 && articleList.value.length < res.total;
        scorllTopCallback?.();
      }
    })
    .catch(() => {
      state.isMore = false;
    })
    .finally(() => {
      state.loading = false;
    });
};

// 使用防抖优化加载更多
const debouncedGetArticleList = debounce(getArticleListFn, 300);

// 加载更多 - 使用节流防止重复点击
const handleLoadMore = throttle(() => {
  if (state.loading || !state.isMore) return;
  state.page.page++;
  debouncedGetArticleList();
}, 1000);

// 分页改变
const handlePageChange = (page: number, size: number, scorllTopCallback?: Function) => {
  state.page.page = page;
  state.page.size = size;
  debouncedGetArticleList(scorllTopCallback);
};

// 获取文章类型
const getArticleTypeList = () => {
  getDict({ dictType: 'article_type' }).then((res: any) => {
    if (res.code === 200) {
      articleTypeList.value = res.data;
      store.SET_ARTICLE_DICT(res.data);
    }
  });
};

// 优化：先看store有没有，没有再请求
if (store.articleDict.length > 0) {
  articleTypeList.value = store.articleDict;
} else {
  getArticleTypeList();
}

// 点击文章详情
const handleArticle = (row: any) => {
  throttledRouterPush('/article/detail', { id: row.id });
};

// 点击阅览
const handleView = (row: any) => {
  throttledRouterPush('/preview', { id: row.id });
};

// 点击文章类型
const handleType = (type: number) => {
  throttledRouterPush('/article/category', { type });
};

// 点击文章分类
const handleCategory = (label: string) => {
  throttledRouterPush('/article/category', { type: 4, category: label });
};

// 使用节流优化resize事件
const resizeCallback = throttle(() => {
  state.isMobile = isMobile(1260);
}, 100);

// 初始化数据
getArticleListFn();

onMounted(() => {
  // 监听resize事件
  window.addEventListener('resize', resizeCallback);
});

onUpdated(() => {
  // 数据加载了之后重新渲染
  live2dReloadElement();
});

// 卸载时清理事件监听器
onUnmounted(() => {
  window.removeEventListener('resize', resizeCallback);
});
</script>

<style lang="scss" scoped>
.home-wrap {
  .other-content-wrap {
    padding-top: 80px;
    background-color: var(--under-background);

    .notice {
      max-width: var(--content-max-width);
      margin: 0 auto;
      max-height: 120px;
    }

    .flex-wrap {
      max-width: 1240px;
      margin: 0 auto;
      display: flex;
      justify-content: center;

      .content-center {
        position: relative;
        display: flex;
        flex-direction: column;
        align-items: center;
        .other-content {
          margin-top: 30px;
          max-width: var(--content-max-width);
          width: 100%;

          .other-content-item {
            .article-title-wrap {
              .article-icon {
                width: 26px;
                height: 26px;
              }
              .content-title {
                margin-top: 10px;
                font-size: 20px;
                font-weight: 600;
                color: var(--text-color);
                text-shadow: var(--text-shadow);
              }
            }
          }
        }
        .m-other-content {
          max-width: var(--content-max-width-m);
        }
      }

      .slider-wrap {
        margin-top: 44px;
        width: 300px;
        margin-left: 20px;

        .position-wrap {
          position: sticky;
          top: 70px;
          padding-bottom: 20px;
        }
      }
    }
  }
}

.m-home-wrap {
  .notice {
    max-width: var(--content-max-width-m) !important;
    margin: 0 auto;
  }

  .flex-wrap {
    flex-direction: column-reverse !important;

    .slider-wrap {
      width: var(--content-max-width-m) !important;
      margin: 0 auto !important;
      margin-top: 30px !important;
    }

    .hot-article .cover-img {
      height: 30vw !important;
    }
  }
}
</style>

<style lang="scss">
.slider-item-wrap {
  width: 100%;
  box-shadow: var(--box-shadow);
  border-radius: var(--border-radius-1);
  position: relative;
  overflow: hidden;
  .slider-title {
    padding-left: 14px;
    height: 44px;
    font-size: 16px;
    line-height: 44px;
    border-bottom: 1px solid #ccc;
    color: #222;
  }
  .mac-style {
    content: ' ';
    position: absolute;
    -webkit-border-radius: 50%;
    border-radius: 50%;
    background: #fc625d !important;
    width: 11px;
    height: 11px;
    -webkit-box-shadow: 20px 0 #fdbc40, 40px 0 #35cd4b;
    box-shadow: 20px 0 #fdbc40, 40px 0 #35cd4b;
    float: right;
    right: 55px;
    top: 17px;
  }
}
</style>
